﻿using GWDataCenter;
using GWDataCenter.Database;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace GWMergeTerminal.STD
{
    public class CEquip : CEquipBase
    {

        static ThreadLocal<Dictionary<string, DeviceMessageSubscriber>> _messageSubscriberCaches = new ThreadLocal<Dictionary<string, DeviceMessageSubscriber>>();

        DeviceMessageSubscriber _messageSubscriber;

        bool _init = false;

        string _targetEquipno;

        int _sleepTime = 50;

        /// <summary>
        /// 接收超时,单位:秒
        /// </summary>
        int _receivedTimeout = 2 * 60;


        private bool indState = false;

        public override bool init(EquipItem item)
        {
            if (!_init || ResetFlag)
            {
                if (base.init(item) == false) return false;

                _targetEquipno = item.Equip_addr;
                // CommonHelper.EquipNo = _targetEquipno;

                if (!int.TryParse(item.communication_time_param.Split('/')[0], out _sleepTime))
                {
                    //默认值
                    _sleepTime = 50;
                }

                if (!int.TryParse(item.communication_time_param.Split('/')[1], out _receivedTimeout))
                {
                    //默认值
                    _receivedTimeout = 2 * 60;
                }
                //user password
                string userName = string.Empty;
                string password = string.Empty;
                if (!string.IsNullOrEmpty(item.communication_param) && item.communication_param.Split(':').Length >= 2)
                {
                    userName = item.communication_param.Split(':')[0];
                    password = item.communication_param.Split(':')[1];
                }

                // ip address and port
                var ip = item.Local_addr.Split(':')[0];
                if (!int.TryParse(item.Local_addr.Split(':')[1], out int port))
                {
                    Utils.LogError($"Local_addr [{item.Local_addr}] config is error!!");
                    return false;
                }


                if (_messageSubscriberCaches.Value == null)
                    _messageSubscriberCaches.Value = new Dictionary<string, DeviceMessageSubscriber>();

                if (_messageSubscriberCaches.Value.ContainsKey($"{ip}:{port}"))
                {
                    _messageSubscriber = _messageSubscriberCaches.Value[$"{ip}:{port}"];
                }
                else
                {
                    _messageSubscriber = new DeviceMessageSubscriber(ip, port, userName, password, _targetEquipno);
                    _messageSubscriberCaches.Value.Add($"{ip}:{port}", _messageSubscriber);
                }
                //是否订阅了获取数据上报
                if (!_messageSubscriber.IsSubscribed(_targetEquipno, TopicString.DeviceDataUp))
                {
                    string topic = $"{TopicString.DeviceDataUp + _targetEquipno}";
                    _messageSubscriber.Subscribe(topic);
                }
                //是否订阅了设备上线
                if (!_messageSubscriber.IsSubscribed(_targetEquipno, TopicString.DeviceState))
                {
                    string topic = $"{TopicString.DeviceState + _targetEquipno}";
                    _messageSubscriber.Subscribe(topic);
                }
                //是否订阅了设备动作执行结果
                if (!_messageSubscriber.IsSubscribed(_targetEquipno, TopicString.DeviceActionResult))
                {
                    string topic = $"{TopicString.DeviceActionResult + _targetEquipno}";
                    _messageSubscriber.Subscribe(topic);
                }
                _init = true;
                ResetFlag = false;

            }

            return true;
        }

        public override CommunicationState GetData(CEquipBase pEquip)
        {
            //判断mqtt连接状态
            if (_messageSubscriber == null || !_messageSubscriber.IsConnected)
                return CommunicationState.fail;

            //判断设备数据是否存在
            if (!_messageSubscriber.yXData.ContainsKey(_targetEquipno) &&
                !_messageSubscriber.dLData.ContainsKey(_targetEquipno) &&
                !_messageSubscriber.dYData.ContainsKey(_targetEquipno) &&
                !_messageSubscriber.gLData.ContainsKey(_targetEquipno))
            {
                LogDebug($"未获取到设备{_targetEquipno}推送的数据(包括遥信、电压、电流、功率)或未建立连接，{_sleepTime}毫秒后尝试重新连接,记录时间：{DateTime.Now}");
                Sleep(_sleepTime);
                //return base.GetData(pEquip);
                return CommunicationState.fail;
            }

            //判断设备数据是否过期
            var deviceMessage = _messageSubscriber.dLData[_targetEquipno];
            var R1 = Convert.ToDateTime(deviceMessage.Timestamp).AddHours(-8);
            var R2 = DateTime.Now - DateTime.Parse(deviceMessage.Timestamp).AddHours(-8);
            var R3 = DateTime.Now - DateTime.Parse(deviceMessage.Timestamp).AddHours(-8) > TimeSpan.FromSeconds(_receivedTimeout);
            if (DateTime.Now - DateTime.Parse(deviceMessage.Timestamp).AddHours(-8) > TimeSpan.FromSeconds(_receivedTimeout))
            {
                LogDebug($"【通讯失败】【{_targetEquipno}】设备数据已过期,序列化后的时间：{DateTime.Parse(deviceMessage.Timestamp).AddHours(-8):yyyy-MM-dd HH:mm:ss}，当前时间：{DateTime.Now:yyyy-MM-dd HH:mm:ss}\n>>>>>>>设置的数据有效时长为{_receivedTimeout}秒  (设置时间不得超过int长度2147483647)\n");
                Sleep(_sleepTime);
                return CommunicationState.fail;
            }
            return base.GetData(pEquip);
        }

        public override bool GetYC(YcpTableRow r)
        {
            var main = r.main_instruction;
            switch (main)
            {
                //下发指令
                case "CMDRSP":
                    try
                    {
                        if (_messageSubscriber.deviceCmdRsp.ContainsKey(_targetEquipno))
                        {
                            var tsetResult = _messageSubscriber.deviceCmdRsp[_targetEquipno];
                            string result = Utils.SerializeObject(tsetResult);
                            SetYCData(r, result);
                            return true;
                        }
                        SetYCData(r, "未收到指令下发回应");
                        return true;
                    }
                    catch (Exception ex)
                    {
                        LogDebug($"下发指令错误：{ ex.Message}");
                        SetYCData(r, "未下发指令回应");
                        return true;
                    }
                //开关状态
                case "Ind0":
                    try
                    {
                        if (_messageSubscriber.yXData.ContainsKey(_targetEquipno))
                        {
                            MqYXMessage yXData = _messageSubscriber.yXData[_targetEquipno];
                            var result = yXData.Body.ServiceProperties.GetValue(main);
                            var values = int.Parse(result.ToString());
                            SetYCData(r, values);
                            return true;
                        }
                        SetYCData(r, "***");
                        return true;
                    }
                    catch (Exception ex)
                    {
                        LogDebug($"开关状态测点错误：{ ex.Message}");
                        SetYCData(r, "未获取到数据");
                        return true;
                    }
                //电流
                case "A_phsA":
                case "A_phsB":
                case "A_phsC":
                    try
                    {
                        if (_messageSubscriber.dLData.ContainsKey(_targetEquipno))
                        {
                            MqDLMessage dLData = _messageSubscriber.dLData[_targetEquipno];
                            var result = dLData.Body.ServiceProperties.GetValue(main);
                            var values = double.Parse(result.ToString());
                            SetYCData(r, values);
                            return true;
                        }
                        SetYCData(r, "***");
                        return true;
                    }
                    catch (Exception ex)
                    {
                        LogDebug($"电流测点错误：{main}-{ex.Message}");
                        SetYCData(r, "未收到数据");
                        return true;
                    }
                //电压
                case "PhV_phsA":
                case "PhV_phsB":
                case "PhV_phsC":
                    try
                    {
                        if (_messageSubscriber.dYData.ContainsKey(_targetEquipno))
                        {
                            MqDYMessage dLData = _messageSubscriber.dYData[_targetEquipno];
                            var result = dLData.Body.ServiceProperties.GetValue(main);
                            var values = double.Parse(result.ToString());
                            SetYCData(r, values);
                            return true;
                        }
                        SetYCData(r, "***");
                        return true;
                    }
                    catch (Exception ex)
                    {
                        LogDebug($"电压测点错误-{ex.Message}");
                        SetYCData(r, "未收到数据");
                        return true;
                    }
                //功率
                case "TotW":
                case "PhW_phsA":
                case "PhW_phsB":
                case "PhW_phsC":
                case "TotPF":
                    try
                    {
                        if (_messageSubscriber.gLData.ContainsKey(_targetEquipno))
                        {
                            MqGLMessage gLData = _messageSubscriber.gLData[_targetEquipno];
                            var result = gLData.Body.ServiceProperties.GetValue(main);
                            var values = double.Parse(result.ToString());
                            SetYCData(r, values);
                            return true;
                        }
                        SetYCData(r, "***");
                        return true;
                    }
                    catch (Exception ex)
                    {
                        LogDebug($"功率测点错误-{ex.Message}");
                        SetYCData(r, "未收到数据");
                        return true;
                    }
                //设备上线
                case "State":
                    try
                    {
                        if (_messageSubscriber.steData.ContainsKey(_targetEquipno))
                        {
                            MqStateMessage steData = _messageSubscriber.steData[_targetEquipno];
                            var result = steData.Body.GetValue(main);
                            SetYCData(r, result);
                            return true;
                        }
                        SetYCData(r, "***");
                        return true;
                    }
                    catch (Exception ex)
                    {
                        LogDebug($"设备上线测点错误-{ex.Message}");
                        SetYCData(r, "未收到数据");
                        return true;
                    }
            }
            LogDebug($"操作命令错误{main}");
            return false;
        }

        public override bool GetYX(YxpTableRow r)
        {
            try
            {
                // 上报的遥信数据
                if (_messageSubscriber.yXData.ContainsKey(_targetEquipno))
                {
                    var deviceMessage = _messageSubscriber.yXData[_targetEquipno];
                    var main = r.main_instruction;
                    switch (main)
                    {
                        case "State":
                            var valueDto = deviceMessage.Body.ServiceProperties;
                            if (valueDto == null)
                            {
                                //LogDebug($"{_targetEquipno}遥信-SetYXData(r, false)；{DateTime.Now}");
                                SetYXData(r, 0);
                            }
                            else
                            {
                                if (!int.TryParse(valueDto.Ind0, out int ind0Value))
                                {
                                    //LogDebug($"{_targetEquipno}Ind0的值为：{valueDto.Ind0}，转int失败");
                                    //LogDebug($"{_targetEquipno}遥信-SetYXData(r, 1)；{DateTime.Now}");
                                    SetYXData(r, 1);
                                }
                                else
                                {
                                    // LogDebug($"{_targetEquipno}遥信-SetYXData(r, {ind0Value})；{DateTime.Now}");
                                    SetYXData(r, ind0Value);
                                }
                            }
                            return true;
                    }
                }
                // 如果该设备有点电流 则为在线  -可能会有不规则的数据的情况出现，故捕捉异常
                else if (_messageSubscriber.dLData.ContainsKey(_targetEquipno))
                {
                    MqDLMessage dLData = _messageSubscriber.dLData[_targetEquipno];
                    if (!double.TryParse(dLData.Body.ServiceProperties.A_phsA, out double a_phsa))
                    {
                        LogDebug($"{_targetEquipno}A_phsA的值为：{dLData.Body.ServiceProperties.A_phsA}，转double失败");
                    }
                    if (!double.TryParse(dLData.Body.ServiceProperties.A_phsB, out double b_phsa))
                    {
                        LogDebug($"{_targetEquipno}A_phsB的值为：{dLData.Body.ServiceProperties.A_phsB}，转double失败");
                    }
                    if (!double.TryParse(dLData.Body.ServiceProperties.A_phsC, out double c_phsa))
                    {
                        LogDebug($"{_targetEquipno}A_phsC的值为：{dLData.Body.ServiceProperties.A_phsC}，转double失败");
                    }

                    if (a_phsa == 0 &&
                        b_phsa == 0 &&
                        c_phsa == 0)
                    {
                        //LogDebug($"{_targetEquipno}电流-SetYXData(r, false)；{DateTime.Now}");
                        SetYXData(r, 0);
                        return true;
                    }
                    else
                    {
                        //LogDebug($"{_targetEquipno}电流-SetYXData(r, 1)；{DateTime.Now}");
                        SetYXData(r, 1);
                        return true;
                    }
                }
                LogDebug($"未收到设备{_targetEquipno}遥信数据，设备离线报警");
                SetYXData(r, 0);
                return true;
            }
            catch (Exception ex)
            {
                LogDebug("获取遥信失败" + ex.Message);
                SetYXData(r, 0);
                return true;
            }
        }

        public override bool SetParm(string MainInstruct, string MinorInstruct, string Value)
        {
            if (_messageSubscriber.IsConnected)
            {
                var minorArray = MinorInstruct.Split(new[] { '-' }, 4);
                //Iot内部使用的命令
                if (minorArray.Length == 4)
                {
                    //遥控
                    if (minorArray[3] == "COMMAND")
                    {
                        int.TryParse(minorArray[0], out int serviceIdValue);
                        int.TryParse(minorArray[1], out int methodValue);
                        int.TryParse(Value, out int ykPoint);
                        CmdContent cmdContent = new CmdContent()
                        {
                            YKtype = serviceIdValue,
                            ykMethod = methodValue,
                            ykpoint = ykPoint
                        };
                        CmdBody cmdBody = new CmdBody()
                        {
                            requestId = Guid.NewGuid().ToString("N"),
                            cmdContent = cmdContent,
                            serviceId = minorArray[2],
                            method = minorArray[3]

                        };
                        string payload = Utils.SerializeObject(new MqCmdMessage()
                        {
                            body = cmdBody,
                            timestamp = DateTime.Now.ToString("yyyy/MM/ddThh:mm:ss"),
                            token = "111"
                        });
                        _messageSubscriber.PublishAsync(
                            TopicString.DeviceAction + MainInstruct//主题
                            , payload);//内容
                        LogCmd($"【发送指令】主题：{TopicString.DeviceAction + MainInstruct}");
                        LogCmd($"【发送指令】消息体：{payload}");
                        return true;
                    }
                }
                //北向直接下发0 --关
                if (Value.Equals("0"))
                {
                    SendYK(2, MainInstruct);
                    return true;
                }
                //北向直接下发1 --开
                else if (Value.Equals("1"))
                {
                    SendYK(4, MainInstruct);
                    return true;
                }
                //Iot内部使用的命令
                else
                {
                    LogCmd($"【发送指令】错误的参数：IOT遥控-->>MinorInstruct:{MinorInstruct}应为Yktype-ykMethod-serviceId-method，\n北向遥控-->>Value:{Value}应输入0或1(sting)，记录时间{DateTime.Now}");
                    DataCenter.WriteLogFile($"【发送指令】错误的参数：IOT遥控-->>MinorInstruct:{MinorInstruct}应为Yktype-ykMethod-serviceId-method，\n北向遥控-->>Value:{Value}北向遥控应输入0或1(sting)，记录时间{DateTime.Now}");
                    return false;
                }
            }
            return false;
        }

        //读写锁，当资源处于写入模式时，其他线程写入需要等待本次写入结束之后才能继续写入
        private ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
        private readonly string logFilePath = "../log/RHZD";
        private readonly DateTime dT = DateTime.Now;

        public void LogDebug(string msg)
        {
            try
            {
                LogWriteLock.EnterWriteLock();
                string fileName = $"../log/RHZD/{dT.Year}-{dT.Month}-{dT.Day}-Rhzd-MQLog.txt";
                if (!Directory.Exists(logFilePath))
                {
                    //创建文件
                    Directory.CreateDirectory(logFilePath);
                }
                //判断文件大小
                using (StreamWriter writer = System.IO.File.AppendText(fileName))
                {
                    writer.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} --{msg}");
                }
            }
            catch (Exception ex)
            {
                DataCenter.WriteLogFile($"写入文件出错！{ex.Message}");
                throw;
            }
            finally
            {
                LogWriteLock.ExitWriteLock();

            }

        }


        /// <summary>
        /// 记录所有操作指令
        /// </summary>
        /// <param name="msg"></param>
        public void LogCmd(string msg)
        {
            try
            {
                LogWriteLock.EnterWriteLock();

                string fileName = $"../log/RHZD/{dT.Year}-{dT.Month}-{dT.Day}-Rhzd-CmdLog.txt";
                if (!Directory.Exists(logFilePath))
                {
                    //创建文件
                    Directory.CreateDirectory(logFilePath);
                }
                using (StreamWriter writer = System.IO.File.AppendText(fileName))
                {
                    writer.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} --{msg}");
                }
            }
            catch (Exception ex)
            {

                DataCenter.WriteLogFile($"写入文件出错！{ex.Message}");
            }
            finally
            {
                LogWriteLock.ExitWriteLock();

            }

        }

        /// <summary>
        /// 设置遥控
        /// </summary>
        /// <param name="ykType">遥控命令4：逆变器开机/开关合   2：关机/开关分</param>
        /// <param name="equipNo"></param>
        public void SendYK(int ykType, string equipNo)
        {
            CmdContent cmdContent = new CmdContent()
            {
                YKtype = ykType,
                ykMethod = 1,
                ykpoint = 0
            };
            CmdBody cmdBody = new CmdBody()
            {
                requestId = Guid.NewGuid().ToString("N"),
                cmdContent = cmdContent,
                serviceId = "Command",
                method = "COMMAND"

            };
            string payload = Utils.SerializeObject(new MqCmdMessage()
            {
                body = cmdBody,
                timestamp = DateTime.Now.ToString("yyyy/MM/ddThh:mm:ss"),
                token = "111"
            });
            _messageSubscriber.PublishAsync(
                TopicString.DeviceAction + equipNo//主题
                , payload);//内容
            LogCmd($"【发送指令】主题：{TopicString.DeviceAction + equipNo}");
            LogCmd($"【发送指令】消息体：{payload}");
            //return true;
        }

    }
}
